import { throttle } from '@tarojs/runtime';
import Taro from '@tarojs/taro';
import { BluetoothErrorHandler, createErrorHandler } from './error-handler';
import { createPacketSplitter, PacketSplitter, transferDataFn } from './packet-splitter';
import { BluetoothOptions, ConnectionState, Device, EmitEvent, EventData } from './types';

export class Bluetooth {
  private _connected: ConnectionState;
  private _deviceList: Device[] = [];
  private _deviceId?: string;
  private _services: Taro.getBLEDeviceServices.BLEService[] = [];
  private _characteristics: Taro.getBLEDeviceCharacteristics.BLECharacteristic[] = [];
  private _eventHandlers: { [K in EmitEvent]?: ((data: EventData<K>) => void)[] } = {};
  private _abortController: ReturnType<typeof createAbortController> | undefined;
  private _options: BluetoothOptions;
  // private _heartbeatTimer?: NodeJS.Timeout;//todo
  public packetSplitter: PacketSplitter;
  public errorHandler: BluetoothErrorHandler;

  constructor(options: BluetoothOptions = {}) {
    this._options = {
      deviceNameFilter: /MyDevice/, // /.*/ 匹配所有设备
      rssiThreshold: -70,
      mtuSize: 512,
      ...options
    };
    this._deviceList = [];
    this._eventHandlers = {};
    this._deviceId = undefined;
    this._connected = ConnectionState.DISCONNECTED;
    this._abortController = undefined;
    this.errorHandler = this._options.createErrorHandler
      ? this._options.createErrorHandler(this)
      : createErrorHandler(this);
    this.packetSplitter = createPacketSplitter({
      chunkSize: 512, // 尝试使用最大MTU
      ...(this._options.splitterOptions || {})
    });
    this.on(EmitEvent.data, (data) => {
      console.log('data', data.value);
    });
  }

  // 初始化蓝牙适配器
  async init() {
    try {
      await this._checkPlatformFeatures();
      await this._openAdapter();
      this._setupEventListeners();
    } catch (err) {
      this._handleError(err);
    }
  }

  async scanDevices(
    options: { timeout?: number; signal?: AbortSignal } = {}
  ): Promise<typeof this._deviceList | undefined> {
    try {
      const { timeout = 10000, signal } = options;

      // 1. 清理并初始化
      this.abortScan();
      this._abortController = createAbortController();
      this._deviceList = [];

      // 2. 确保蓝牙适配器就绪
      await this._openAdapter();

      // 3. 开始设备发现
      await this._startDiscovery();
      return await new Promise<typeof this._deviceList>((resolve, reject) => {
        // 4. 超时处理
        const timer = setTimeout(() => {
          this._stopDiscovery().then(() => resolve(this._deviceList));
        }, timeout);
        const onDeviceFound = (res: Taro.onBluetoothDeviceFound.CallbackResult) => {
          this._handleDeviceFound(res.devices);
        };
        // 5. 取消信号处理
        signal?.addEventListener('abort', () => {
          this._stopDiscovery(timer, onDeviceFound);
          reject(new DOMException('Scan aborted', 'AbortError'));
        });
        // 添加节流处理
        const throttleDeviceFound = throttle(onDeviceFound, 500);
        Taro.onBluetoothDeviceFound(throttleDeviceFound);
      });
    } catch (err) {
      await this._stopDiscovery();
      this._handleError(err);
    }
  }

  // 连接设备
  async connect(deviceId?: string) {
    try {
      if (!deviceId) {
        throw new Error('无效的设备ID');
      }
      await this._stopDiscovery();
      await Taro.createBLEConnection({ deviceId });
      this._deviceId = deviceId;
      this._connected = ConnectionState.CONNECTED;
      await this._discoverServices();
    } catch (err) {
      this._handleError(err);
    }
  }

  // 发现蓝牙特征值
  async discoverCharacteristics(serviceId: string) {
    // 前置条件检查
    if (!this._deviceId) {
      throw new Error('设备未连接，请先建立连接');
    }
    if (!serviceId?.trim()) {
      throw new Error('无效的服务ID');
    }
    try {
      const { characteristics } = await Taro.getBLEDeviceCharacteristics({
        deviceId: this._deviceId,
        serviceId
      });
      this._characteristics = characteristics;
      return characteristics;
    } catch (err) {
      this._handleError(err);
    }
  }

  /**
   * 启用通知
   * @param {string} serviceId
   * @param {string} characteristicId
   */
  async startNotification(serviceId: string, characteristicId: string) {
    try {
      if (!this._deviceId) {
        throw new Error('设备未连接，请先建立连接');
      }
      await Taro.notifyBLECharacteristicValueChange({
        deviceId: this._deviceId,
        serviceId,
        characteristicId,
        state: true
      });
    } catch (error) {
      this._handleError(error);
    }
  }

  /**
   * 断开连接
   */
  async disconnect() {
    try {
      if (this._connected === ConnectionState.DISCONNECTED || !this._deviceId) return;
      await Taro.closeBLEConnection({ deviceId: this._deviceId });
      this._connected = ConnectionState.DISCONNECTED;
      this._deviceId = undefined;
    } catch (error) {
      this._handleError(error);
    }
  }

  // 写入数据（支持重试）
  async write(
    serviceId: string,
    characteristicId: string,
    value: ArrayBuffer,
    options: {
      retry?: number;
    } = {}
  ) {
    const { retry = 2 } = options;
    let attempts = 0;
    while (attempts <= retry) {
      try {
        await Taro.writeBLECharacteristicValue({
          deviceId: this._deviceId!,
          serviceId,
          characteristicId,
          value: value
        });
        return;
      } catch (err) {
        if (attempts++ >= retry) {
          this._handleError(err);
        } else {
          await new Promise((resolve) => setTimeout(resolve, 300));
        }
      }
    }
  }

  async send(data: Uint8Array, fn: transferDataFn, packetSequenceStart = 1) {
    if (!this._deviceId) {
      throw new Error('设备未连接，请先建立连接');
    }
    return this.packetSplitter.send(data, fn, packetSequenceStart);
  }

  async setMTU(size = 512) {
    try {
      await Taro.setBLEMTU({ deviceId: this._deviceId!, mtu: size });
    } catch (error) {
      this._handleError(error);
    }
  }

  // 手动取消扫描
  abortScan() {
    if (this._abortController) {
      this._abortController.abort();
      this._abortController = undefined;
    }
  }

  // 获取当前状态
  getState() {
    return {
      deviceId: this._deviceId,
      connected: this._connected,
      services: [...this._services],
      characteristics: [...this._characteristics],
      devices: [...this._deviceList]
    };
  }

  // 事件监听
  on<K extends EmitEvent>(event: K, listener: (data: EventData<K>) => void): this {
    if (!this._eventHandlers[event]) {
      this._eventHandlers[event] = [];
    }
    this._eventHandlers[event].push(listener);
    return this;
  }

  once<K extends EmitEvent>(event: K, listener: (data: EventData<K>) => void): this {
    const onceListener = (data: EventData<K>) => {
      listener(data);
      this.off(event, onceListener);
    };
    return this.on(event, onceListener);
  }
  // 移除监听
  off<K extends EmitEvent>(event: K, listener: (data: EventData<K>) => void): this {
    const handlers = this._eventHandlers[event];
    if (handlers) {
      this._eventHandlers[event] = (handlers as any[]).filter((h) => h !== listener);
    }
    return this;
  }

  // 私有方法
  private async _checkPlatformFeatures() {
    if (!Taro.openBluetoothAdapter) {
      throw new Error('当前环境不支持蓝牙功能');
    }
  }

  private async _openAdapter() {
    const getAdapterState = async () => {
      try {
        const res = await Taro.getBluetoothAdapterState();
        const _available = res.available || (res as any).adapterState.available;
        return !!_available;
      } catch (err) {
        return false;
      }
    };
    try {
      let _available = await getAdapterState();
      if (_available) {
        return true;
      }
      // 打开蓝牙适配器
      await Taro.openBluetoothAdapter();
      // 获取完整状态
      return await getAdapterState();
    } catch (error) {
      this._handleError(error);
    }
  }

  private _setupEventListeners() {
    Taro.onBLEConnectionStateChange((res) => {
      this._connected = res.connected ? ConnectionState.CONNECTED : ConnectionState.DISCONNECTED;
      this._emit(EmitEvent.connectionStateChange, res);
    });

    Taro.onBLECharacteristicValueChange((res) => {
      this._emit(EmitEvent.data, res);
    });

    Taro.onBluetoothAdapterStateChange((res) => {
      this._emit(EmitEvent.adapterStateChange, res);
    });
  }

  private async _startDiscovery() {
    try {
      await Taro.startBluetoothDevicesDiscovery({
        allowDuplicatesKey: true,
        interval: 1000,
        powerLevel: 'high'
      });
    } catch (error) {
      this._handleError(error);
    }
  }

  private async _stopDiscovery(timer?: any, listener?: any) {
    clearTimeout(timer);
    if (listener) Taro.offBluetoothDeviceFound(listener);
    try {
      await Taro.stopBluetoothDevicesDiscovery();
      // Android兼容性延迟
      await new Promise((resolve) => setTimeout(resolve, 100));
    } catch (err) {
      this._handleError(err);
    }
  }

  private _handleDeviceFound(devices: Device[]) {
    const validDevices = devices.filter(
      (device) =>
        device.name?.match(this._options.deviceNameFilter!) &&
        (device.RSSI ?? -Infinity) > (this._options.rssiThreshold ?? -70)
    );
    if (validDevices.length > 0) {
      // 去重合并新设备
      validDevices.forEach((newDevice) => {
        const exists = this._deviceList.some((d) => d.deviceId === newDevice.deviceId);
        if (!exists) {
          this._deviceList.push(newDevice);
          this._emit(EmitEvent.deviceFound, newDevice);
        }
      });
    }
  }

  private async _discoverServices() {
    const { services } = await Taro.getBLEDeviceServices({ deviceId: this._deviceId! });
    this._services = services;
  }

  private _emit<K extends EmitEvent>(event: K, data: EventData<K>) {
    const handlers = this._eventHandlers[event];
    handlers?.forEach((handler) => handler(data));
  }

  private _handleError(err: TaroGeneral.BluetoothError | Error) {
    const errorMap = {
      10000: '未初始化蓝牙适配器',
      10001: '当前蓝牙适配器不可用',
      10002: '没有找到指定设备',
      10003: '连接失败',
      10004: '没有找到指定服务',
      10005: '没有找到指定特征值',
      10006: '当前连接已断开',
      10007: '当前特征值不支持此操作',
      10008: '其余所有系统上报的异常',
      10009: '系统特有错误：iOS特有异常',
      10010: '连接超时'
    };
    (err as any).errMsg = errorMap[(err as any).errCode] || `${(err as any).errMsg}`;
    if (this.errorHandler) {
      this.errorHandler.handle(err);
    }
    throw err;
  }
}

// 兼容小程序的 abortController 实现
function createAbortController() {
  if (typeof AbortController !== 'undefined') {
    return new AbortController();
  }

  // 小程序环境下的降级实现
  return {
    signal: { aborted: false },
    abort() {
      this.signal.aborted = true;
      if (this.onabort) this.onabort();
    },
    onabort: null
  };
}
